﻿#include "praxis/core/parameter_system.h"

#include <algorithm>
#include <filesystem>  // C++17 for path manipulation
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <regex>
#include <set>
#include <shared_mutex>
#include <sstream>
#include <vector>

namespace fs = std::filesystem;

namespace praxis {
class ValueNodeImpl;
class ArrayNodeImpl;
struct ConfigManagerImpl;

// --- Command Pattern for Undo/Redo ---
class ICommand {
 public:
  virtual ~ICommand() = default;
  virtual void Execute() = 0;
  virtual void Undo() = 0;
};

class SetValueCommand : public ICommand {
 public:
  SetValueCommand(ValueNodeImpl* node, json old_value, json new_value);

  void Execute() override;  // Implemented after ValueNodeImpl
  void Undo() override;

 private:
  ValueNodeImpl* node_;
  json old_value_;
  json new_value_;
};

// --- Command for AddElement ---
class AddElementCommand : public ICommand {
 public:
  AddElementCommand(ArrayNodeImpl* parent, size_t index);

  void Execute()
      override;  // AddElement 已经被调用, 所以 Execute 为空或用于 Redo
  void Undo() override;

 private:
  ArrayNodeImpl* parent_;
  size_t index_;
};

// --- Command for RemoveElement ---
class RemoveElementCommand : public ICommand {
 public:
  RemoveElementCommand(ArrayNodeImpl* parent,
                       std::unique_ptr<IParameterNode> removed_node,
                       size_t index);

  void Execute() override;  // Redo
  void Undo() override;

 private:
  ArrayNodeImpl* parent_;
  std::unique_ptr<IParameterNode>
      removed_node_;  // Command owns the node while it's "removed"
  size_t index_;
};

// Base Implementation Class for Nodes
class ParameterNodeImpl : public virtual IParameterNode {
 protected:
  std::string path_;
  json metadata_;
  bool readonly_;
  ConfigManagerImpl* owner_;
  std::map<ObserverHandle, ParameterObserver> observers_;
  ObserverHandle next_observer_id_ =
      1;  // Start at 1, 0 can be an invalid/default handle

 public:
  ParameterNodeImpl(std::string path, json metadata, ConfigManagerImpl* owner)
      : path_(std::move(path)), metadata_(std::move(metadata)), owner_(owner) {
    readonly_ = metadata_.value("readonly", false);
  }
  ~ParameterNodeImpl() override = default;

  const std::string& GetPath() const override { return path_; }
  const json& GetMetadata() const override { return metadata_; }
  bool IsReadOnly() const override { return readonly_; }

  ObserverHandle AddObserver(ParameterObserver observer) override;
  void RemoveObserver(ObserverHandle handle) override;

  // These must be virtual and should be overridden
  ParameterType GetType() const { return ParameterType::OBJECT; }
  IValueNode* AsValue() override { return nullptr; }
  IArrayNode* AsArray() override { return nullptr; }
  const IValueNode* AsValue() const override { return nullptr; }
  const IArrayNode* AsArray() const override { return nullptr; }

  // Internal access for implementations
  void SetPath(std::string new_path) { path_ = std::move(new_path); }

 protected:
  ConfigManagerImpl* GetOwner() const override { return owner_; }
  /**
   * @brief Notifies all registered observers that this node has changed.
   * This method assumes a write lock is already held by the calling function.
   * It safely copies the observers before notifying to prevent issues if an
   * observer tries to modify the observer list during its callback.
   */
  void NotifyObservers() {
    // Create a temporary copy of the observers to call. This prevents iterator
    // invalidation if an observer's callback tries to add/remove another
    // observer.
    std::vector<ParameterObserver> observers_to_notify;
    observers_to_notify.reserve(observers_.size());
    for (const auto& pair : observers_) {
      observers_to_notify.push_back(pair.second);
    }

    // Now, call the observers from the safe copy.
    // The 'this' pointer points to the derived node (ValueNodeImpl or
    // ArrayNodeImpl).
    for (const auto& observer_func : observers_to_notify) {
      if (observer_func) {
        observer_func(this);
      }
    }
  }
};

// --- Value Node Implementation ---
class ValueNodeImpl : public ParameterNodeImpl, public IValueNode {
 private:
  json value_;

 public:
  ValueNodeImpl(std::string path, json metadata, ConfigManagerImpl* owner)
      : ParameterNodeImpl(std::move(path), std::move(metadata), owner) {
    // Set initial value to the default specified in the schema
    value_ = metadata_.value("default", json());
  }

  // IParameterNode
  ParameterType GetType() const override { return ParameterType::VALUE; }
  IValueNode* AsValue() override { return this; }
  const IValueNode* AsValue() const override { return this; }

  // ValueNode
  json GetValueJson() const override { return value_; }
  bool SetValueJson(const json& value, ErrorInfo& error) override;

  // Internal setter used by commands and loading to bypass command creation
  void DoSetValue(json new_value) {
    value_ = std::move(new_value);
    NotifyObservers();
  }

 private:
  friend class SetValueCommand;
  friend struct ConfigManagerImpl;

  bool Validate(const json& value, ErrorInfo& error) {
    // --- 1. Type Check ---
    if (metadata_.contains("type")) {
      const auto& expected_type_str = metadata_["type"].get<std::string>();
      bool type_ok =
          (expected_type_str == "number" && value.is_number()) ||
          (expected_type_str == "integer" && value.is_number_integer()) ||
          (expected_type_str == "string" && value.is_string()) ||
          (expected_type_str == "boolean" && value.is_boolean()) ||
          (expected_type_str == "object" && value.is_object());
      if (!type_ok) {
        error = {ErrorInfo::Code::ValidationFailedType,
                 "Type mismatch for '" + path_ + "'. Expected '" +
                     expected_type_str + "', but got a different type."};
        return false;
      }
    }

    // --- 2. Enum Check ---
    if (metadata_.contains("enum")) {
      const auto& enum_values = metadata_["enum"];
      if (std::find(enum_values.begin(), enum_values.end(), value) ==
          enum_values.end()) {
        error = {ErrorInfo::Code::ValidationFailedEnum,
                 "Value '" + value.dump() + "' for '" + path_ +
                     "' is not in the allowed list of values."};
        return false;
      }
    }
    // --- 3. Numeric Range Check (for numbers and integers) ---
    if (value.is_number()) {
      if (metadata_.contains("minimum")) {
        if (value < metadata_["minimum"]) {
          error = {ErrorInfo::Code::ValidationFailedRange,
                   "Value " + value.dump() + " for '" + path_ +
                       "' is less than the minimum of " +
                       metadata_["minimum"].dump()};
          return false;
        }
      }
      if (metadata_.contains("maximum")) {
        if (value > metadata_["maximum"]) {
          error = {ErrorInfo::Code::ValidationFailedRange,
                   "Value " + value.dump() + " for '" + path_ +
                       "' is greater than the maximum of " +
                       metadata_["maximum"].dump()};
          return false;
        }
      }
    }

    // --- 4. String Pattern Check (using regex) ---
    // Requires <regex> header
    if (value.is_string() && metadata_.contains("pattern")) {
      try {
        std::regex pattern(metadata_["pattern"].get<std::string>());
        if (!std::regex_match(value.get<std::string>(), pattern)) {
          error = {ErrorInfo::Code::ValidationFailedRegex,
                   "Value '" + value.get<std::string>() + "' for '" + path_ +
                       "' does not match the required pattern."};
          return false;
        }
      } catch (const std::regex_error& e) {
        // The pattern in the schema itself is invalid
        error = {
            ErrorInfo::Code::SchemaError,
            "Invalid regex pattern in schema for '" + path_ + "': " + e.what()};
        return false;  // This is a schema error, not a user value error
      }
    }

    // TODO: Add other checks here like minLength, maxLength, etc. if needed.

    // NOTE: A real implementation would add range checks, etc.
    error = {ErrorInfo::Code::Ok, "Validation successful"};
    return true;
  }
};

//==============================================================================
// COMMAND IMPLEMENTATIONS (need full Node/Manager defs)
//==============================================================================
SetValueCommand::SetValueCommand(ValueNodeImpl* node, json old_value,
                                 json new_value)
    : node_(node),
      old_value_(std::move(old_value)),
      new_value_(std::move(new_value)) {}
void SetValueCommand::Execute() { node_->DoSetValue(new_value_); }
void SetValueCommand::Undo() { node_->DoSetValue(old_value_); }

class ArrayNodeImpl : public ParameterNodeImpl, public IArrayNode {
  friend struct ConfigManagerImpl;

 private:
  std::vector<IParameterNode*> elements_;  // Non-owning pointers
  json element_schema_;

 public:
  ArrayNodeImpl(std::string path, const json& metadata,
                ConfigManagerImpl* owner)
      : ParameterNodeImpl(std::move(path), std::move(metadata), owner) {
    element_schema_ = metadata_.value("items", json::object());
  }

  // --- 接口实现 ---
  ParameterType GetType() const override { return ParameterType::ARRAY; }
  IArrayNode* AsArray() override { return this; }
  const IArrayNode* AsArray() const override { return this; }

  // --- ArrayNode 接口实现 ---
  size_t GetSize() const override;
  IParameterNode* GetElement(size_t index) override;
  const IParameterNode* GetElement(size_t index) const override;
  IParameterNode* AddElement() override;
  bool RemoveElement(size_t index) override;
  void Clear() override;

 private:
  friend class AddElementCommand;
  friend class RemoveElementCommand;
  friend class ClearCommand;

  // void NotifyObservers() {
  //   for (auto observer : observers_) {
  //     observer->OnNodeChanged(this);
  //   }
  // }
};

// Recursive helper to find all external references in a JSON document
// and trigger their loading.
bool find_and_load_refs(const fs::path& current_file_path, json& doc,
                        std::set<std::string>& visited_in_chain,
                        ConfigManagerImpl* owner, ErrorInfo& error);
// --- Manager Implementation ---

struct ConfigManagerImpl {
  // Core data structures
  std::map<std::string, std::unique_ptr<IParameterNode>>
      node_storage_;  // Sole owner of all nodes
  std::map<std::string, IParameterNode*, std::less<>> node_path_map_;
  std::map<std::string, IParameterNode*> root_properties_;

  // Multi-file schema support
  std::map<std::string, json> loaded_schemas_;
  json root_schema_;
  std::string schema_version_ = "unknown";

  // Transaction & Undo/Redo
  bool in_transaction_ = false;
  std::vector<std::unique_ptr<ICommand>> undo_stack_;
  std::vector<std::unique_ptr<ICommand>> redo_stack_;

  // The central mutex for thread safety.
  // It's mutable to allow locking in const methods for read access.
  mutable std::shared_mutex mutex_;

  // --- Core Logic ---
  void Reset();
  IParameterNode* ParseNodeDefinition(const json& doc_context,
                                      const json& schema,
                                      const std::string& path,
                                      ErrorInfo& error);
  const json* ResolveRef(const json& current_doc, const std::string& ref,
                         ErrorInfo& error);
  bool LoadSchemaRecursive(const std::string& filepath,
                           std::set<std::string>& visited, ErrorInfo& error);

  // --- Node Management Helpers (centralized and safe) ---
  IParameterNode* CreateElementForArray(ArrayNodeImpl* parent);
  std::unique_ptr<IParameterNode> RemoveElementFromArray(ArrayNodeImpl* parent,
                                                         size_t index);
  void InsertElementIntoArray(ArrayNodeImpl* parent,
                              std::unique_ptr<IParameterNode> node,
                              size_t index);
  void UpdateElementPaths(ArrayNodeImpl* parent, size_t start_index);

  void AddCommand(std::unique_ptr<ICommand> command);

  // --- Static Helpers for Serialization ---
  static void LoadValuesRecursive(const json& json_node,
                                  IParameterNode* param_node, ErrorInfo& error);
  static std::optional<json> SaveNodeToJson(IParameterNode* node);
};

// --- Method Implementations That Depend on ConfigManagerImpl ---

bool ValueNodeImpl::SetValueJson(const json& value, ErrorInfo& error) {
  std::unique_lock lock(owner_->mutex_);
  // 1. Check if the node is read-only
  if (IsReadOnly()) {
    error = {ErrorInfo::Code::ReadonlyViolation,
             "Cannot set value for read-only parameter '" + path_ + "'."};
    return false;
  }

  // 2. Validate the new value against the schema
  if (!Validate(value, error)) {
    // error.code = ErrorInfo::Code::ValueInvalid;
    //  The error info is already set by the Validate function
    return false;
  }

  // 3. Check if the value is actually changing to avoid unnecessary operations
  if (value == value_) {
    error = {ErrorInfo::Code::Ok, "Value is already set."};
    return true;  // Not a failure, but nothing to do.
  }

  // 4. If inside a transaction, create a command for undo/redo
  if (owner_->in_transaction_) {
    // Create a command, execute it, and add to undo stack
    owner_->AddCommand(std::make_unique<SetValueCommand>(this, value_, value));
  }

  // Not in transaction, just apply the change
  DoSetValue(value);

  error = {ErrorInfo::Code::Ok, "Value set successfully."};
  return true;
}

// --- ConfigManager Public Interface Implementation ---

std::unique_ptr<ConfigManager> ConfigManager::Create() {
  return std::unique_ptr<ConfigManager>(new ConfigManager());
}
ConfigManager::ConfigManager() : impl_(std::make_unique<ConfigManagerImpl>()) {}
ConfigManager::~ConfigManager() = default;
ConfigManager::ConfigManager(ConfigManager&&) noexcept = default;
ConfigManager& ConfigManager::operator=(ConfigManager&&) noexcept = default;

[[nodiscard]] bool ConfigManager::LoadDefinitions(
    const std::string& definition_filepath, ErrorInfo& error) {
  std::unique_lock lock(impl_->mutex_);

  impl_->Reset();

  // This set is used for cycle detection within a single `LoadDefinitions`
  // call.
  std::set<std::string> visited_files;

  // Start the recursive loading process.
  if (!impl_->LoadSchemaRecursive(definition_filepath, visited_files, error)) {
    impl_->Reset();  // Clean up partial state on failure
    return false;
  }

  // After successful loading, get the canonical path of the root file to
  // retrieve it from the cache.
  fs::path canonical_path;
  try {
    canonical_path = fs::weakly_canonical(definition_filepath);
  } catch (...) {
    // This should not fail as LoadSchemaRecursive already succeeded with this
    // path.
    error = {ErrorInfo::Code::FileNotFound,
             "Could not re-resolve root schema path after loading: " +
                 definition_filepath};
    return false;
  }

  // Retrieve the fully-loaded root schema from the cache.
  impl_->root_schema_ = impl_->loaded_schemas_[canonical_path.string()];
  impl_->schema_version_ =
      impl_->root_schema_.value("$schemaVersion", "unknown");

  // Proceed with parsing the properties of the root schema.
  const auto& properties =
      impl_->root_schema_.value("properties", json::object());
  for (const auto& [key, prop_schema] : properties.items()) {
    IParameterNode* root_prop_node = impl_->ParseNodeDefinition(
        impl_->root_schema_, prop_schema, key, error);
    if (!root_prop_node) {  // 检查返回值
      impl_->Reset();
      return false;
    }
    impl_->root_properties_[key] = root_prop_node;
  }

  return true;
}

bool ConfigManager::LoadValues(const std::string& values_filepath,
                               ErrorInfo& error) {
  std::unique_lock lock(impl_->mutex_);

  // 检查1: 定义必须先加载
  if (impl_->root_properties_.empty()) {
    error = {ErrorInfo::Code::SchemaError,
             "Cannot load values before definitions."};
    return false;
  }

  // 检查2: 文件是否存在
  std::ifstream file(values_filepath);
  if (!file.is_open()) {
    error = {ErrorInfo::Code::FileNotFound,
             "Values file not found: " + values_filepath};
    return false;
  }

  // 检查3: JSON是否能解析
  json values_doc;
  try {
    values_doc = json::parse(file);
  } catch (const json::parse_error& e) {
    error = {ErrorInfo::Code::JsonParseError,
             "Failed to parse values file: " + std::string(e.what())};
    return false;
  }

  // 检查4: 版本匹配
  // 读取 Values 文件中的版本并进行比较
  std::string values_version = values_doc.value("$schemaVersion", "unknown");
  if (values_version != impl_->schema_version_) {
    // 版本不匹配，设置一个警告信息，但继续执行
    error = {ErrorInfo::Code::VersionMismatch,
             "Warning: Values file version ('" + values_version +
                 "') does not match loaded schema version ('" +
                 impl_->schema_version_ +
                 "'). Proceeding with best-effort load."};
    // 注意：我们不在此处 'return false'，这实现了策略B（宽松模式）
  }

  // 从顶层属性开始递归加载
  for (const auto& [key, node_ptr] : impl_->root_properties_) {
    if (values_doc.contains(key)) {
      ConfigManagerImpl::LoadValuesRecursive(values_doc[key], node_ptr, error);
      if (error) return false;
    }
  }
  return true;
}

bool ConfigManager::LoadFromValuesFile(const std::string& values_filepath,
                                       ErrorInfo& error) {
  impl_->Reset();

  // Step 1: Pre-read values file to find the "$schema" reference
  std::ifstream values_file(values_filepath);
  if (!values_file.is_open()) {
    error = {ErrorInfo::Code::FileNotFound,
             "Values file not found: " + values_filepath};
    return false;
  }

  json values_doc;
  try {
    values_doc = json::parse(values_file);
    // We close the file stream here and will re-open it in LoadValues for
    // simplicity. An optimization could be to pass the parsed `values_doc` to
    // `LoadValues`.
    values_file.close();
  } catch (const json::parse_error& e) {
    error = {ErrorInfo::Code::JsonParseError,
             "Failed to parse values file for schema lookup: " +
                 std::string(e.what())};
    return false;
  }

  if (!values_doc.contains("$schema") || !values_doc["$schema"].is_string()) {
    error = {
        ErrorInfo::Code::SchemaError,
        "Values file does not contain a valid '$schema' string reference: " +
            values_filepath};
    return false;
  }
  const std::string schema_ref_path_str =
      values_doc["$schema"].get<std::string>();

  // Step 2: Resolve the absolute path of the schema file
  fs::path schema_full_path;
  try {
    fs::path values_file_path(values_filepath);
    fs::path base_dir = values_file_path.parent_path();
    schema_full_path = fs::absolute(base_dir / schema_ref_path_str);
    schema_full_path = fs::weakly_canonical(schema_full_path);
  } catch (const fs::filesystem_error& e) {
    error = {ErrorInfo::Code::FileNotFound, "Error resolving schema path '" +
                                                schema_ref_path_str +
                                                "': " + e.what()};
    return false;
  }

  // Step 3: Use the resolved path to load the definitions (this will handle
  // multi-file schemas)
  if (!LoadDefinitions(schema_full_path.string(), error)) {
    // LoadDefinitions will have filled the error info, so we just propagate it.
    return false;
  }

  // Step 4: After definitions are loaded, load the values.
  // We pass the original filepath, LoadValues will handle opening and parsing
  // it again.
  if (!LoadValues(values_filepath, error)) {
    // LoadValues will have filled the error info.
    return false;
  }

  return true;  // Success!
}

bool ConfigManager::SaveValues(const std::string& values_filepath,
                               ErrorInfo& error) {
  // This is now a convenience wrapper for saving the whole tree (empty path).
  return SaveValues(values_filepath, "", error);
}

bool ConfigManager::SaveValues(const std::string& values_filepath,
                               const std::string& path, ErrorInfo& error) {
  std::unique_lock lock(impl_->mutex_);

  json content_to_save = json::object();

  // 1. 从顶层属性开始递归保存
  if (path.empty()) {
    for (const auto& [key, node_ptr] : impl_->root_properties_) {
      if (auto node_json = ConfigManagerImpl::SaveNodeToJson(node_ptr)) {
        content_to_save[key] = *node_json;
      } else {
        // 如果一个节点无法序列化，可以决定是忽略还是报错
        // 这里选择忽略
      }
    }
  } else {
    // Save a specific subtree
    IParameterNode* start_node = FindNode(path);
    if (!start_node) {
      error = {ErrorInfo::Code::NodeNotFound,
               "Path to save not found: " + path};
      return false;
    }

    auto subtree_json = ConfigManagerImpl::SaveNodeToJson(start_node);
    if (!subtree_json) {
      error = {ErrorInfo::Code::TransactionError,
               "Failed to serialize subtree at path: " + path};
      return false;
    }

    // Reconstruct the path structure in the output JSON
    json final_json;
    json* current_level = &final_json;
    std::stringstream ss(path);
    std::string segment;
    std::vector<std::string> path_segments;

    // Split path by '.'
    while (std::getline(ss, segment, '.')) {
      path_segments.push_back(segment);
    }

    for (size_t i = 0; i < path_segments.size() - 1; ++i) {
      current_level = &((*current_level)[path_segments[i]]);
    }
    (*current_level)[path_segments.back()] = *subtree_json;
    content_to_save = final_json;
  }

  // 2. Wrap the content with metadata and write to file
  json output_json;
  // Add the $schema reference back, so the saved file is also self-describing.
  // To do this, we need the path to the root schema file relative to the output
  // file.
  try {
    fs::path root_schema_path(
        impl_->root_schema_.value("praxis:self_path", ""));
    fs::path output_file_path(values_filepath);
    if (!root_schema_path.empty()) {
      fs::path relative_schema_path =
          fs::relative(root_schema_path, output_file_path.parent_path());
      output_json["$schema"] = relative_schema_path.generic_string();
    }
  } catch (const fs::filesystem_error&) {
    // fallback if relative path calculation fails
  }

  // 3. 写入版本信息, 合并数据
  output_json["$schemaVersion"] = impl_->schema_version_;
  output_json.update(content_to_save);  // Merge the actual data

  // 4. 写入文件
  std::ofstream out_file(values_filepath);
  if (!out_file.is_open()) {
    error = {ErrorInfo::Code::FileNotFound,
             "Could not open file for writing: " + values_filepath};
    return false;
  }

  // 5. 使用 std::setw(2) 进行格式化输出，美观易读
  out_file << std::setw(2) << output_json << std::endl;
  if (out_file.fail()) {
    error = {ErrorInfo::Code::TransactionError,
             "Failed to write all data to file: " + values_filepath};
    return false;
  }

  return true;
}

std::string ConfigManager::GetSchemaVersion() const {
  return impl_->schema_version_;
}

IParameterNode* ConfigManager::FindNode(const std::string& path) {
  std::shared_lock lock(impl_->mutex_);

  auto it = impl_->node_path_map_.find(path);
  return (it != impl_->node_path_map_.end()) ? it->second : nullptr;
}

const IParameterNode* ConfigManager::FindNode(const std::string& path) const {
  std::shared_lock lock(impl_->mutex_);

  auto it = impl_->node_path_map_.find(path);
  return (it != impl_->node_path_map_.end()) ? it->second : nullptr;
}

// A simple UI-like observer that just prints changes
class ConsoleObserver : public IParameterObserver {
 public:
  void OnNodeChanged(const IParameterNode* node) override {
    std::cout << "[OBSERVER]: Node '" << node->GetPath() << "' changed. ";
    if (auto v_node = node->AsValue()) {
      std::cout << "New value: " << v_node->GetValueJson().dump() << std::endl;
    }
  }
};

void ConfigManager::PrintNodeInfo(IParameterNode* node) {
  if (!node) {
    std::cout << "  [Node is null]" << std::endl;
    return;
  }

  std::cout << "  Path: " << node->GetPath() << std::endl;

  if (auto* v_node = node->AsValue()) {
    std::cout << "    Type: Value, Current Value: "
              << v_node->GetValueJson().dump() << std::endl;
  } else if (auto* a_node = node->AsArray()) {
    std::cout << "    Type: Array, Size: " << a_node->GetSize() << std::endl;
    for (size_t i = 0; i < a_node->GetSize(); ++i) {
      std::cout << "    - Element " << i << ":" << std::endl;
      // 打印子节点的路径，这里我们就不递归打印所有信息了
      std::cout << "      Path: " << a_node->GetElement(i)->GetPath()
                << std::endl;
    }
  }
}

int ConfigManager::Test() {
  // Schema definition in-memory for simplicity
  const char* schema_content = R"({
        "properties": {
            "speed": { "type": "number", "default": 100.0, "minimum": 0, "maximum": 500 },
            "mode": { "type": "string", "default": "auto", "enum": ["auto", "manual"] }
        }
    })";
  std::ofstream("schema.json") << schema_content;

  auto config_manager = ConfigManager::Create();
  ErrorInfo error;

  // 1. Load with error handling
  std::cout << "1. Loading definitions..." << std::endl;
  if (!config_manager->LoadDefinitions("schema.json", error)) {
    std::cerr << "Load failed: " << error.message << std::endl;
    return 1;
  }

  auto speed_node = config_manager->FindNode("speed")->AsValue();
  auto mode_node = config_manager->FindNode("mode")->AsValue();
  assert(speed_node && mode_node);

  // 2. Attach an observer
  std::cout << "2. Attaching observer..." << std::endl;
  ConsoleObserver observer;
  auto observer_handle =
      speed_node->AddObserver([&observer](const IParameterNode* node) {
        observer.OnNodeChanged(node);
      });

  std::cout << "  Setting speed to 150 (observer should trigger)..."
            << std::endl;
  speed_node->SetValueJson(150.0, error);

  // 3. Use Metadata
  std::cout << "3. Using Metadata..." << std::endl;
  const auto& speed_meta = speed_node->GetMetadata();
  std::cout << "  Speed metadata 'maximum': " << speed_meta.value("maximum", 0)
            << std::endl;

  // 4. Test Validation
  std::cout << "4. Testing Validation..." << std::endl;
  std::cout << "  Trying to set mode to 'invalid_mode'..." << std::endl;
  if (!mode_node->SetValueJson("invalid_mode", error)) {
    std::cout << "  SUCCESS: SetValue failed as expected. Reason: "
              << error.message << std::endl;
  }

  // 5. Use Transaction and Undo/Redo
  std::cout << "5. Testing Transaction, Undo, and Redo..." << std::endl;
  config_manager->BeginTransaction();
  std::cout << "  Transaction started." << std::endl;

  std::cout << "  Setting speed to 200..." << std::endl;
  speed_node->SetValueJson(200.0, error);
  std::cout << "  Setting speed to 300..." << std::endl;
  speed_node->SetValueJson(300.0, error);
  PrintNodeInfo(speed_node);

  std::cout << "  Can Undo? " << std::boolalpha << config_manager->CanUndo()
            << std::endl;

  std::cout << "  Undoing last change..." << std::endl;
  config_manager->Undo();
  PrintNodeInfo(speed_node);

  std::cout << "  Undoing again..." << std::endl;
  config_manager->Undo();
  PrintNodeInfo(speed_node);

  std::cout << "  Can Redo? " << std::boolalpha << config_manager->CanRedo()
            << std::endl;
  std::cout << "  Redoing..." << std::endl;
  config_manager->Redo();
  PrintNodeInfo(speed_node);

  // 6. Cancel a transaction
  std::cout << "6. Testing Cancel Transaction..." << std::endl;
  std::cout << "  Current state: ";
  PrintNodeInfo(speed_node);
  std::cout << "  Setting speed to 999..." << std::endl;
  speed_node->SetValueJson(999.0, error);
  std::cout << "  New state: ";
  PrintNodeInfo(speed_node);
  std::cout << "  Canceling transaction..." << std::endl;
  config_manager
      ->CancelTransaction();  // This will undo the "999" and the "200"
  std::cout << "  State after cancel: ";
  PrintNodeInfo(speed_node);  // Reverts to state before transaction

  // Demonstrate commit
  std::cout << "7. Committing a transaction..." << std::endl;
  config_manager->BeginTransaction();
  speed_node->SetValueJson(450.0, error);
  std::cout << "  State after change: ";
  PrintNodeInfo(speed_node);
  config_manager->CommitTransaction();
  std::cout << "  Transaction committed." << std::endl;

  speed_node->RemoveObserver(observer_handle);
  std::cout << "--- Demo Finished ---" << std::endl;

  return 0;
}
// --- Transaction Methods ---
void ConfigManager::BeginTransaction() {
  std::unique_lock lock(impl_->mutex_);
  impl_->in_transaction_ = true;
  impl_->undo_stack_.clear();
  impl_->redo_stack_.clear();
  // In a real scenario, you might serialize the current state here for Cancel
}

void ConfigManager::CommitTransaction() {
  std::unique_lock lock(impl_->mutex_);
  impl_->in_transaction_ = false;
  // Clearing stacks is optional, could allow undo past commit
}

void ConfigManager::CancelTransaction() {
  std::unique_lock lock(impl_->mutex_);
  while (!impl_->undo_stack_.empty()) {
    auto cmd = std::move(impl_->undo_stack_.back());
    impl_->undo_stack_.pop_back();
    cmd->Undo();
    // We don't populate redo_stack during a cancel
  }
  impl_->in_transaction_ = false;
}

bool ConfigManager::CanUndo() const {
  std::shared_lock lock(impl_->mutex_);
  return !impl_->undo_stack_.empty();
}

bool ConfigManager::CanRedo() const {
  std::shared_lock lock(impl_->mutex_);
  return !impl_->redo_stack_.empty();
}

void ConfigManager::Undo() {
  std::unique_lock lock(impl_->mutex_);
  if (impl_->undo_stack_.empty()) return;
  auto cmd = std::move(impl_->undo_stack_.back());
  impl_->undo_stack_.pop_back();
  cmd->Undo();
  impl_->redo_stack_.push_back(std::move(cmd));
}

void ConfigManager::Redo() {
  std::unique_lock lock(impl_->mutex_);
  if (impl_->redo_stack_.empty()) return;
  auto cmd = std::move(impl_->redo_stack_.back());
  impl_->redo_stack_.pop_back();
  cmd->Execute();
  impl_->undo_stack_.push_back(std::move(cmd));
}

size_t ArrayNodeImpl::GetSize() const { return elements_.size(); }

IParameterNode* ArrayNodeImpl::GetElement(size_t index) {
  return (index < elements_.size()) ? elements_[index] : nullptr;
}

const IParameterNode* ArrayNodeImpl::GetElement(size_t index) const {
  return (index < elements_.size()) ? elements_[index] : nullptr;
}

IParameterNode* ArrayNodeImpl::AddElement() {
  std::unique_lock lock(owner_->mutex_);
  return owner_->CreateElementForArray(this);
}

bool ArrayNodeImpl::RemoveElement(size_t index) {
  std::unique_lock lock(owner_->mutex_);
  if (index >= GetSize()) {
    return false;
  }

  // 执行实际操作
  auto removed_node = owner_->RemoveElementFromArray(this, index);
  if (!removed_node) return false;

  // 如果在事务中，创建命令并把节点所有权交给命令
  if (owner_->in_transaction_) {
    owner_->AddCommand(std::make_unique<RemoveElementCommand>(
        this, std::move(removed_node), index));
  }
  // If not in transaction, the unique_ptr is simply destroyed here, freeing
  // memory.
  return true;
}

void ArrayNodeImpl::Clear() {
  std::unique_lock lock(owner_->mutex_);
  // This can be implemented as repeated RemoveElement calls for undo/redo
  // integration
  while (!elements_.empty()) {
    size_t index_to_remove = elements_.size() - 1;
    auto removed_node = owner_->RemoveElementFromArray(this, index_to_remove);
    if (!removed_node) {
      return; 
    }
    if (owner_->in_transaction_) {
      owner_->AddCommand(std::make_unique<RemoveElementCommand>(
          this, std::move(removed_node), index_to_remove));
    }
  }
}

void ConfigManagerImpl::Reset() {
  // 按特定顺序清理以避免悬空指针问题
  undo_stack_.clear();
  redo_stack_.clear();
  root_properties_.clear();
  node_path_map_.clear();
  node_storage_.clear();
  loaded_schemas_.clear();
  root_schema_ = {};
  schema_version_.clear();
  in_transaction_ = false;
}

IParameterNode* ConfigManagerImpl::ParseNodeDefinition(const json& doc_context,
                                                       const json& schema,
                                                       const std::string& path,
                                                       ErrorInfo& error) {
  const json* effective_schema = &schema;
  if (schema.contains("$ref")) {
    effective_schema =
        ResolveRef(doc_context, schema["$ref"].get<std::string>(), error);
    if (!effective_schema) {
      // ResolveRef 已经设置了错误信息
      return nullptr;
    }
  }

  std::string type_str = effective_schema->value("type", "object");
  std::unique_ptr<IParameterNode> new_node;

  if (type_str == "array") {
    new_node = std::make_unique<ArrayNodeImpl>(path, *effective_schema, this);
  } else if (type_str == "object") {
    new_node =
        std::make_unique<ParameterNodeImpl>(path, *effective_schema, this);
  } else {  // "value", "string", "number", "integer", "boolean"
    new_node = std::make_unique<ValueNodeImpl>(path, *effective_schema, this);
  }

  if (!new_node) {
    error = {ErrorInfo::Code::SchemaError,
             "Node creation failed for path '" + path + "'."};
    return nullptr;
  }

  // 关键步骤：获取裸指针，然后立即将所有权转移给 Manager。
  IParameterNode* raw_ptr = new_node.get();
  node_path_map_[path] = raw_ptr;
  node_storage_[path] =
      std::move(new_node);  // 此后 new_node 为空，但 raw_ptr 仍然有效！

  // 如果是 Object 节点，需要递归创建其子节点
  if (type_str == "object") {
    if (effective_schema->contains("properties")) {
      for (auto& [key, prop_schema] :
           (*effective_schema)["properties"].items()) {
        std::string child_path = path.empty() ? key : path + "." + key;
        // 递归调用
        if (!ParseNodeDefinition(doc_context, prop_schema, child_path, error)) {
          // 如果子节点创建失败, 这是一个部分成功的状态。
          // 理想情况下应该回滚(删除已创建的父节点)，但为简化起见，先传播错误。
          return nullptr;
        }
      }
    }
  }

  return raw_ptr;  // 返回一个有效的、由 manager 拥有的裸指针。
}

const json* ConfigManagerImpl::ResolveRef(const json& current_doc,
                                          const std::string& ref,
                                          ErrorInfo& error) {
  // 1. 将 $ref 字符串分解为文件路径部分和 JSON 指针部分
  auto hash_pos = ref.find('#');
  std::string filepath_part =
      (hash_pos != std::string::npos) ? ref.substr(0, hash_pos) : ref;
  std::string pointer_part =
      (hash_pos != std::string::npos) ? ref.substr(hash_pos) : "";

  // 2. 确定要搜索的目标 JSON 文档
  const json* target_doc = &current_doc;

  if (!filepath_part.empty()) {
    // 这是一个跨文件引用，我们需要在 loaded_schemas_ 缓存中查找

    // a. 获取当前文档的路径，以便解析相对路径
    std::string current_doc_path_str =
        current_doc.value("praxis:self_path", "");
    if (current_doc_path_str.empty()) {
      error = {ErrorInfo::Code::SchemaError,
               "Cannot resolve relative cross-file reference from a schema "
               "that has no path information."};
      return nullptr;
    }

    // b. 计算被引用文件的绝对路径
    fs::path ref_absolute_path;
    try {
      fs::path base_path = fs::path(current_doc_path_str).parent_path();
      ref_absolute_path = fs::weakly_canonical(base_path / filepath_part);
    } catch (const fs::filesystem_error& e) {
      error = {ErrorInfo::Code::SchemaError,
               "Failed to resolve referenced file path '" + filepath_part +
                   "': " + e.what()};
      return nullptr;
    }

    // c. 在已加载的 schemas 缓存中查找
    auto it = loaded_schemas_.find(ref_absolute_path.string());
    if (it == loaded_schemas_.end()) {
      // 这个错误意味着 LoadSchemaRecursive 的预加载步骤失败或不完整
      error = {ErrorInfo::Code::SchemaError, "Referenced schema file '" +
                                                 ref_absolute_path.string() +
                                                 "' was not pre-loaded."};
      return nullptr;
    }
    target_doc = &it->second;
  }

  // 3. 在目标文档中解析 JSON 指针
  if (pointer_part.empty() || pointer_part == "#") {
    // 如果指针部分为空或只有'#'，则引用整个文档
    return target_doc;
  }

  try {
    // nlohmann::json::json_pointer 需要一个以 '/' 开头的路径
    json::json_pointer ptr(pointer_part.substr(1));
    return &target_doc->at(ptr);
  } catch (const json::exception& e) {
    error = {ErrorInfo::Code::SchemaError, "Failed to resolve JSON pointer '" +
                                               pointer_part +
                                               "' in document: " + e.what()};
    return nullptr;
  }
}

bool ConfigManagerImpl::LoadSchemaRecursive(
    const std::string& filepath, std::set<std::string>& visited_in_chain,
    ErrorInfo& error) {
  // 1. Canonicalize the path to get a unique identifier for caching
  fs::path canonical_path;
  try {
    canonical_path = fs::weakly_canonical(filepath);
  } catch (const fs::filesystem_error& e) {
    error = {
        ErrorInfo::Code::FileNotFound,
        "Failed to resolve schema path: " + filepath + " (" + e.what() + ")"};
    return false;
  }
  std::string canonical_path_str = canonical_path.string();

  // 2. Cycle Detection: Check if we've already seen this file in this specific
  // call chain.
  if (visited_in_chain.count(canonical_path_str)) {
    return true;  // Cycle detected, but this is not an error. Just stop
                  // recursing.
  }
  visited_in_chain.insert(canonical_path_str);

  // 3. Cache Check: Check if this file has already been loaded and parsed
  // globally.
  if (loaded_schemas_.count(canonical_path_str)) {
    return true;  // Already loaded in a previous, independent chain.
  }

  // 4. Load and Parse File
  std::ifstream file(canonical_path);
  if (!file.is_open()) {
    error = {ErrorInfo::Code::FileNotFound,
             "Schema file not found: " + canonical_path_str};
    return false;
  }

  json doc;
  try {
    doc = json::parse(file);
  } catch (const json::parse_error& e) {
    error = {
        ErrorInfo::Code::JsonParseError,
        "Failed to parse schema file " + canonical_path_str + ": " + e.what()};
    return false;
  }

  // 5. Pre-scan for dependencies and load them recursively BEFORE adding the
  // current doc to the cache. This ensures that when the current doc is used,
  // its dependencies are already available.
  if (!find_and_load_refs(canonical_path, doc, visited_in_chain, this, error)) {
    return false;  // Propagate dependency loading failure
  }

  // 6. Store in Cache and Mark as complete
  // We add a custom metadata field to remember the file's own path.
  // This is useful for SaveValues to write a relative $schema path.
  doc["praxis:self_path"] = canonical_path_str;
  loaded_schemas_[canonical_path_str] = std::move(doc);

  // Backtrack for cycle detection: remove from the current chain tracker before
  // returning up the stack.
  visited_in_chain.erase(canonical_path_str);

  return true;
}

void ConfigManagerImpl::AddCommand(std::unique_ptr<ICommand> command) {
  if (!in_transaction_) return;
  undo_stack_.push_back(std::move(command));
  redo_stack_.clear();  // New action clears the redo history
}

IParameterNode* ConfigManagerImpl::CreateElementForArray(
    ArrayNodeImpl* parent) {
  // ParseNodeDefinition needs this, but we'll ignore errors for now
  ErrorInfo error;
  size_t new_index = parent->GetSize();
  std::string new_path =
      parent->GetPath() + "[" + std::to_string(new_index) + "]";

  IParameterNode* new_node_ptr = ParseNodeDefinition(
      root_schema_, parent->element_schema_, new_path, error);

  if (!new_node_ptr) {
    return nullptr;
  }

  parent->elements_.push_back(new_node_ptr);

  if (in_transaction_) {
    AddCommand(std::make_unique<AddElementCommand>(parent, new_index));
  }

  parent->NotifyObservers();

  return new_node_ptr;
}

std::unique_ptr<IParameterNode> ConfigManagerImpl::RemoveElementFromArray(
    ArrayNodeImpl* parent, size_t index) {
  if (index >= parent->GetSize()) return nullptr;

  IParameterNode* node_to_remove = parent->GetElement(index);
  if (!node_to_remove) { 
    // LOG_ERROR("Null pointer found in array elements at index " << index);
    return nullptr;
  }
  const std::string path_to_remove = node_to_remove->GetPath();

  // C++17: Safely extract the node from the map, transferring ownership
  auto node_handle = node_storage_.extract(path_to_remove);
  if (node_handle.empty()) {
    parent->elements_.erase(parent->elements_.begin() + index);
    UpdateElementPaths(parent, index);  // 仍然需要更新路径
    parent->NotifyObservers();
    return nullptr;  // Should not happen if state is consistent
  }

  node_path_map_.erase(path_to_remove);
  parent->elements_.erase(parent->elements_.begin() + index);

  // Update paths of subsequent elements
  UpdateElementPaths(parent, index);

  parent->NotifyObservers();

  return std::move(node_handle.mapped());
}

void ConfigManagerImpl::InsertElementIntoArray(
    ArrayNodeImpl* parent, std::unique_ptr<IParameterNode> node, size_t index) {
  // Insert into vector of
  IParameterNode* raw_ptr = node.get();
  const std::string node_path = node->GetPath();

  // Insert into vector of non-owning pointers
  parent->elements_.insert(parent->elements_.begin() + index, raw_ptr);

  // Put the node back into the main storage, restoring manager's ownership
  node_path_map_[node_path] = raw_ptr;
  node_storage_[node_path] = std::move(node);

  // Update paths for all elements that were shifted
  UpdateElementPaths(
      parent,
      index);  // The inserted element will also get its path updated if needed

  parent->NotifyObservers();
}

void ConfigManagerImpl::UpdateElementPaths(ArrayNodeImpl* parent,
                                           size_t start_index) {
  for (size_t i = start_index; i < parent->elements_.size(); ++i) {
    IParameterNode* node = parent->elements_[i];
    std::string new_path = parent->GetPath() + "[" + std::to_string(i) + "]";

    if (node->GetPath() != new_path) {
      if (auto impl_node = dynamic_cast<ParameterNodeImpl*>(node)) {
        // 1. 从旧路径映射中移除
        node_path_map_.erase(impl_node->GetPath());

        // 2. 在节点对象内部设置新路径
        impl_node->SetPath(new_path);

        // 3. 将新路径添加到映射中
        node_path_map_[new_path] = impl_node;

        // 4. 更新核心存储中的键，保持一致性 (这是之前版本中的一个关键修复)
        auto node_handle =
            node_storage_.extract(node->GetPath());  // 用旧路径提取
        if (!node_handle.empty()) {
          node_handle.key() = new_path;                  // 修改键为新路径
          node_storage_.insert(std::move(node_handle));  // 重新插入
        }
      } else {
        // 如果 dynamic_cast 失败，这是一个严重的逻辑错误，应该被记录或断言
        // 在我们的设计中，所有节点都应该是 ParameterNodeImpl
        // 的实例，所以这里不应该失败
      }
    }
  }
}

// 主递归函数
void ConfigManagerImpl::LoadValuesRecursive(const json& json_node,
                                            IParameterNode* param_node,
                                            ErrorInfo& error) {
  if (!param_node) return;

  // 根据节点类型选择不同的加载策略
  switch (param_node->GetType()) {
    case ParameterType::VALUE: {
      if (auto value_node = param_node->AsValue()) {
        // SetValueJson 内部处理类型转换和验证
        if (!value_node->SetValueJson(json_node, error)) {
          // It's a readonly violation or validation error
          error = {
              ErrorInfo::Code::ReadonlyViolation,
              "Cannot set read-only value at path: " + param_node->GetPath()};
        }
      }
      break;
    }
    case ParameterType::ARRAY: {
      if (auto array_node = param_node->AsArray()) {
        if (!json_node.is_array()) break;  // 类型不匹配，跳过
        array_node->Clear();               // 清空现有所有元素
        for (const auto& item_json : json_node) {
          IParameterNode* new_element = array_node->AddElement();
          if (new_element) {
            // 对新创建的元素进行递归加载
            LoadValuesRecursive(item_json, new_element, error);
          }
        }
      }
      break;
    }
    case ParameterType::OBJECT: {  // 对象类型节点的处理
      if (!json_node.is_object()) break;
      for (auto const& [key, val] : json_node.items()) {
        std::string child_path = param_node->GetPath().empty()
                                     ? key
                                     : param_node->GetPath() + "." + key;
        auto it = param_node->GetOwner()->node_path_map_.find(child_path);
        if (it != param_node->GetOwner()->node_path_map_.end()) {
          LoadValuesRecursive(val, it->second, error);
        }
      }
      break;
    }
  }
}

// 主递归函数
std::optional<json> ConfigManagerImpl::SaveNodeToJson(IParameterNode* node) {
  if (!node) return std::nullopt;

  switch (node->GetType()) {
    case ParameterType::VALUE: {
      if (auto value_node = node->AsValue()) {
        return std::make_optional(value_node->GetValueJson());
      }
      break;
    }
    case ParameterType::ARRAY: {
      if (auto array_node = node->AsArray()) {
        json json_array = json::array();
        for (size_t i = 0; i < array_node->GetSize(); ++i) {
          if (auto elem_json = SaveNodeToJson(array_node->GetElement(i))) {
            json_array.push_back(*elem_json);
          }
        }
        return json_array;
      }
      break;
    }
    case ParameterType::OBJECT: {
      json object_json = json::object();
      // This is tricky. We need to find all children.
      const std::string prefix =
          node->GetPath().empty() ? "" : node->GetPath() + ".";
      for (auto const& [path, child_node] : node->GetOwner()->node_path_map_) {
        if (!prefix.empty() && path.rfind(prefix, 0) != 0) continue;
        // Get the local key part of the path
        std::string key = path.substr(prefix.length());
        if (key.find('.') != std::string::npos ||
            key.find('[') != std::string::npos)
          continue;  // direct child only

        if (auto child_json = SaveNodeToJson(child_node)) {
          object_json[key] = *child_json;
        }
      }
      return object_json;
    }
  }
  return std::nullopt;
}

// --- Command 实现 ---
// 这些实现现在可以安全地调用 ArrayNodeImpl 的方法了

AddElementCommand::AddElementCommand(ArrayNodeImpl* parent, size_t index)
    : parent_(parent), index_(index) {}

void AddElementCommand::Execute() {
  parent_->GetOwner()->CreateElementForArray(parent_);
}

void AddElementCommand::Undo() {
  parent_->GetOwner()->RemoveElementFromArray(parent_, index_);
}

RemoveElementCommand::RemoveElementCommand(
    ArrayNodeImpl* parent, std::unique_ptr<IParameterNode> removed_node,
    size_t index)
    : parent_(parent), removed_node_(std::move(removed_node)), index_(index) {}

void RemoveElementCommand::Execute() /* Redo */ {
  removed_node_ = parent_->GetOwner()->RemoveElementFromArray(parent_, index_);
}

void RemoveElementCommand::Undo() {
  parent_->GetOwner()->InsertElementIntoArray(parent_, std::move(removed_node_),
                                              index_);
}

bool find_and_load_refs(const fs::path& current_file_path, json& doc,
                        std::set<std::string>& visited_in_chain,
                        ConfigManagerImpl* owner, ErrorInfo& error) {
  // Case 1: The document is a JSON object
  if (doc.is_object()) {
    // Iterate through all key-value pairs of the object
    for (auto it = doc.begin(); it != doc.end(); ++it) {
      // Check if the current key is "$ref" and its value is a string
      if (it.key() == "$ref" && it.value().is_string()) {
        std::string ref_str = it.value();
        // An external ref is one that doesn't start with '#'
        if (ref_str.empty() || ref_str[0] == '#') {
          continue;  // Skip local refs, they are resolved by the main loader
        }

        auto hash_pos = ref_str.find('#');
        std::string ref_filepath_str = ref_str.substr(0, hash_pos);

        // Resolve the path of the referenced file relative to the current file
        fs::path ref_path = current_file_path.parent_path() / ref_filepath_str;
        ref_path =
            ref_path
                .lexically_normal();  // Normalize path (e.g., a/b/../c -> a/c)

        // Recursively load the dependency
        if (!owner->LoadSchemaRecursive(ref_path.string(), visited_in_chain,
                                        error)) {
          // If a dependency fails to load, propagate the failure up
          return false;
        }
      } else {
        // If it's not a $ref key, recurse into the value to find nested $refs
        if (!find_and_load_refs(current_file_path, it.value(), visited_in_chain,
                                owner, error)) {
          return false;
        }
      }
    }
  }
  // Case 2: The document is a JSON array
  else if (doc.is_array()) {
    // Iterate through all elements of the array
    for (auto& element : doc) {
      // We only need to recurse into each element. There are no keys to check.
      if (!find_and_load_refs(current_file_path, element, visited_in_chain,
                              owner, error)) {
        return false;
      }
    }
  }

  // Case 3: The document is a primitive (string, number, bool, null). Do
  // nothing.
  return true;
}

ObserverHandle ParameterNodeImpl::AddObserver(ParameterObserver observer) {
  // Lock exclusively as we are modifying the node's internal state (the
  // observers list).
  std::unique_lock lock(owner_->mutex_);
  ObserverHandle handle = next_observer_id_++;
  observers_[handle] = std::move(observer);
  return handle;
}

void ParameterNodeImpl::RemoveObserver(ObserverHandle handle) {
  // Lock exclusively to modify the observers list.
  std::unique_lock lock(owner_->mutex_);
  observers_.erase(handle);
}
}  // namespace praxis